part of 'widgets.dart';

mixin VanProvide {}

class VanProvider<T> extends InheritedWidget {
  final T? provide;

  const VanProvider({super.key, this.provide, required super.child});

  static T? of<T>(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<VanProvider<T>>()?.provide;
  }

  @override
  bool updateShouldNotify(covariant VanProvider<T> oldWidget) => oldWidget.provide != provide;
}

const _spaceStr = " ";
final _spaceRegExp = RegExp(r"\s+");

class VanCss<T> {
  final dynamic value;

  const VanCss([this.value]);

  static final varRegExp = RegExp(r"^var\((?<varKey>--van-.*)\)");

  T? get(BuildContext context, [VanCssKey? key, int hashCode = 0]) {
    final v = _extraVarValue(context, value);
    if (v == null && !kReleaseMode && key != null) {
      _logger.i("[$hashCode-1]$key:$value", stackTrace: StackTrace.empty);
      return null;
    }
    final v2 = _getValueWithType<T>(v);
    if (v2 == null && !kReleaseMode && key != null) {
      _logger.i("[$hashCode-2]$key:$v", stackTrace: StackTrace.empty);
    }
    return v2;
  }

  static E? _getValueWithType<E>(value) {
    var val = value;
    if (val == null) return null;
    if (E == String) {
      val = getString(val);
    } else if (E == int) {
      val = getInt(val);
    } else if (E == double) {
      val = getDouble(val);
    } else if (E == Color) {
      val = getColor(val);
    } else if (E == Gradient) {
      val = getGradient(val);
    } else if (E == EdgeInsets) {
      val = getEdgeInsets(val);
    } else if (E == VanBgColor) {
      val = getBackground(val);
    } else if (E == BorderRadius) {
      val = getBorderRadius(val);
    } else {
      val = null;
    }
    return val;
  }

  _extraVarValue(BuildContext context, dynamic value) {
    if (value == null) return null;
    if (value is! String) return value;
    if (T == EdgeInsets && value.contains(_spaceStr)) {
      final split = value.trim().split(_spaceRegExp);
      final values = split.map((e) {
        var v = _extraVarValue(context, e.trim());
        return _getValueWithType<EdgeInsets>(v);
      }).toList();
      if (values.isEmpty) return null;
      if (values.length == 1) return values.firstOrNull;

      final maxIndex = values.length - 1;
      return EdgeInsets.only(
        top: (values[0])!.top,
        right: (values[1])!.right,
        bottom: (values[maxIndex > 2 ? 2 : 0])!.bottom,
        left: (values[maxIndex > 3 ? 3 : 1])!.left,
      );
    }
    if (T == BorderRadius && value.contains(_spaceStr)) {
      final split = value.trim().split(_spaceRegExp);
      final values = split.map((e) {
        var v = _extraVarValue(context, e.trim());
        return _getValueWithType<double>(v);
      }).toList();
      if (values.isEmpty) return null;
      if (values.length == 1) return BorderRadius.all(Radius.circular(values[0]!));
      return BorderRadius.all(Radius.elliptical(values[0]!, values[1]!));
    }
    if (varRegExp.hasMatch(value)) {
      final varKey = varRegExp.firstMatch(value)!.namedGroup("varKey")!;
      final val = VanCssKey._auto(varKey).get(context);
      return val;
    }
    return value;
  }
}

extension StringOfVanCssKey on String {
  vck() {
    return VanCssKey(this);
  }

  vanCssKey() {
    return VanCssKey(this);
  }
}

enum VanDoubleType { px, em, auto }

@immutable
class VanDouble {
  final dynamic _value;
  final VanDoubleType type;

  const VanDouble(dynamic value, {this.type = VanDoubleType.auto}) : _value = value;

  const VanDouble.em(dynamic value)
      : _value = value,
        type = VanDoubleType.em;

  const VanDouble.px(dynamic value)
      : _value = value,
        type = VanDoubleType.px;
}

@immutable
class VanBgColor with VanProvide {
  final dynamic value;
  static const _empty = VanBgColor.empty();

  bool get isColor => value is Color;

  bool get isGradient => value is Gradient;

  Color? get color => value is Color ? value : null;

  Gradient? get gradient => value is Gradient ? value : null;

  Color? get firstColor => value is Color
      ? value
      : value != null && value is Gradient
          ? (value as Gradient).colors.firstOrNull
          : null;

  const VanBgColor.empty() : value = null;

  const VanBgColor.color(Color color) : value = color;

  const VanBgColor.gradient(Gradient gradient) : value = gradient;

  factory VanBgColor(dynamic value) {
    if (value == null) return _empty;
    if (value is VanBgColor) return value;
    if (value is Color) return VanBgColor.color(value);
    if (value is Gradient) return VanBgColor.gradient(value);

    final gradient = getGradient(value);
    if (gradient != null) return VanBgColor.gradient(gradient);
    final color = getColor(value);
    if (color != null) return VanBgColor.color(color);

    throw ArgumentError("VanBackground.value must be Color or Gradient!");
  }
}

Widget? createWidget(dynamic widget, {TextStyle? style, Type? type, String? tag}) {
  if (widget == null) return null;
  Widget? widget0;
  if (widget is Widget) {
    widget0 = widget;
  } else if (widget is String || widget is int || widget is double) {
    widget0 = Text("$widget");
  } else {
    if (kDebugMode) {
      if (type != null && tag != null) {
        String name = type.toString();
        if (name.startsWith("_")) name = name.substring(1);
        if (name.endsWith("State")) name = name.substring(0, name.length - 5);
        _logger.e("$name.$tag type only support [Widget] or [String] or [int] or [double]!");
      } else {
        _logger.e("createWidget type only support [Widget] or [String] or [int] or [double]!");
      }
    }
    return null;
  }

  if (style != null) {
    widget0 = DefaultTextStyle(style: style, child: widget0);
  }
  return widget0;
}

T? parseEnum<T extends Enum>(dynamic value, {required List<T> values, Type? type, String? tag, T? orElse}) {
  for (var v in values) {
    if (v.name == value) return v;
    if (v.index == value) return v;
  }
  if (type != null && tag != null) {
    _logger.e("${type.runtimeType}.$tag illegal value(see [${T.runtimeType}])!.");
  }
  if (orElse != null) return orElse;
  return null;
}
